import java.io.BufferedWriter;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import org.omg.CORBA.PUBLIC_MEMBER;

public class UTMforRecommendation extends TopicModel {

	public HashMap<Integer, Integer> wordMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> wordMapInv = new HashMap<Integer, Integer>();;

	public HashMap<Integer, Integer> auMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> auMapInv = new HashMap<Integer, Integer>();;

	public HashMap<Integer, Integer> usMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> usMapInv = new HashMap<Integer, Integer>();;

	public HashMap<Integer, Integer> mMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> mMapInv = new HashMap<Integer, Integer>();;

	public HashMap<Integer, Integer> rMap = new HashMap<Integer, Integer>();;
	public HashMap<Integer, Integer> rMapInv = new HashMap<Integer, Integer>();;

	public Integer[] moviesId;
	public Integer[] groupsId;
	public Integer[] usersId;

	public int[][] movies;
	public int[][] movieUsers;
	public int[][] groupUsers;
	public int[][] userMovies;
	public int[][] umRating;
	public int[][] moviesU;
	public int[][] moviesZ;
	public double [][]  pUm;
	
	public int[][][] nUZW;
	public int[][] nUZ;
	public int[][] nuUZ;
	public int[] nU;
	public int[][] nMZ;
	public int[] nM;

	public double[][] npUZ;
	public double[][] npMZ;
	public double[][] npGZ;
	public double[][] sUM;
	public double[][] sMU;
	public double [][] sGM;
	public double [][] sMG;
	public double [][] sUG;
	public double [][] sGU;
	public double [][] sMGU;
	public double [][] sMUG;

	public int M;
	public int W;
	public int Z;
	public int G;
	public int U;
	public int Ug;
	public int Um;
	public int Mu;


	public double beta;
	public double alpha;

	public UTMforRecommendation(int z, double a, double b) {
		alpha = a;
		beta = b;
		Z = z;
	}

	@Override
	public void initialize() {
		System.out.println("Initializing...");
		Random r = new Random();

		moviesZ = new int[M][];
		moviesU = new int[M][];

		nUZW = new int[U][Z][W];
		nUZ = new int[U][Z];
		nuUZ = new int[U][Z];
		nU = new int[U];
		nMZ = new int[M][Z];
		nM = new int[M];
		npUZ = new double[U][Z]; // distributions of topics over users
		npMZ = new double[M][Z];// distributions of topics over movies
		npGZ = new double[G][Z];
		sUM= new double [U][M];
		sMU= new double [M][U];
		sGM= new double [G][M];
		sMG= new double [M][G];
		sUG= new double [U][G];
		sGU= new double [G][U];
		sMGU= new double [M][U];
		sMUG= new double [M][G];
		pUm= new double [U][M];

		for (int m = 0; m < M; m++) {
			moviesZ[m] = new int[movies[m].length];
			moviesU[m] = new int[movies[m].length];

			for (int n = 0; n < movies[m].length; n++) {
				int w = movies[m][n];

				int u = r.nextInt(movieUsers[m].length); // select random a
															// value in
															// {0...A-1}
				int user = movieUsers[m][u];
				moviesU[m][n] = user;

				int z = r.nextInt(this.Z); // select random z value in {0...Z-1}
				moviesZ[m][n] = z;

				// update counts
				nU[user] += 1;
				nuUZ[user][z] += 1;
				nUZ[user][z] += 1;
				nUZW[user][z][w] += 1;
				nMZ[m][z] += 1;
				nM[m] += 1;
			}
		}
	}

	@Override
	public void doSampling() {
		for (int m = 0; m <M; m++) {
			for (int n = 0; n < movies[m].length; n++) {
				int w = movies[m][n];
				int topic = moviesZ[m][n];
				int user = moviesU[m][n];

				// decrement counts
				nU[user] -= 1;
				nuUZ[user][topic] -= 1;
				nUZ[user][topic] -= 1;
				nUZW[user][topic][w] -= 1;
				nMZ[m][topic] -= 1;
				nM[m] -= 1;

				double betaNorm = W * beta;

				// sample new value for author

				double pTotal = 0.0;
				double[] p = new double[U];

				for (int u = 0; u < this.movieUsers[m].length; u++) {
					
					user = this.movieUsers[m][u];
					
					int sumUmr=0;
					if (umRating[user][m]>0){
					for (int um=0; um< userMovies[user].length; um++){
						sumUmr=sumUmr+umRating[user][um];
					}
					
//					System.out.println(pUm[user][m] +"**************");
			         pUm[user][m] = umRating[user][m]/sumUmr;
					p[u] = pUm[user][m]*(nuUZ[user][topic] + alpha);

					pTotal += p[u];
					}
					else p[u] =0; 
				}

				Random r = new Random();
				double e = r.nextDouble() * pTotal;

				double v = 0;
				for (int u = 0; u < U; u++) {
					v += p[u];

					if (v > e) {
						user = this.movieUsers[m][u];
						break;
					}
				}
				// sample new value for topic

				pTotal = 0.0;
				p = new double[Z];

				for (int z = 0; z < Z; z++) {
					p[z] = (nUZW[user][z][w] + beta)
							/ (nUZ[user][z] + betaNorm)
							* (nuUZ[user][z] + alpha);

					pTotal += p[z];
				}

				r = new Random();
				e = r.nextDouble() * pTotal;

				double f = 0;
				for (int z = 0; z < Z; z++) {
					f += p[z];

					if (f > e) {
						topic = z;
						break;
					}
				}
				// increment counts

				nU[user] += 1;
				nuUZ[user][topic] += 1;
				nUZ[user][topic] += 1;
				nUZW[user][topic][w] += 1;
				nMZ[m][topic] += 1;
				nM[m] += 1;

				// set new assignments

				moviesZ[m][n] = topic;
				moviesU[m][n] = user;

			}
		}
		likelihood();
	}

	public void rATM_unRWR(){

		for (int u = 0; u < U; u++) {
			for (int z = 0; z < Z; z++) {
				npUZ[u][z] = (nuUZ[u][z]+alpha) / (nU[u]+U*alpha); // distributions of topics over
													// users
			}
		}

		for (int m = 0; m < M; m++) {
			for (int z = 0; z < Z; z++) {
				npMZ[m][z] = (nMZ[m][z]+beta) / (nM[m]+Z*beta); // distributions of topics over
												// documents
			}
		}
		
		for (int g=0; g<G; g++){
			for (int z=0; z<Z; z++){
				int sumUm=0;
				for(int u=0; u<groupUsers[g].length; u++){
					sumUm= sumUm + userMovies[u].length;
					npGZ[g][z]=npGZ[g][z]+(userMovies[u].length/sumUm)* npUZ[u][z];// distributions of topics over groups
				}	
			}
		}
    // similarity of users and movies
		for (int u = 0; u < U; u++) {
			for (int m = 0; m < M; m++) {
				sUM[u][m] = 0;
				double put = 0;
				double pmt = 0;
				for (int z = 0; z < Z; z++) {
					sUM[u][m] = sUM[u][m] + npUZ[u][z] * npMZ[m][z];
					put = put + npUZ[u][z] * npUZ[u][z];
					pmt = pmt + npMZ[m][z] * npMZ[m][z];
				}
				put = Math.sqrt(put);
				pmt = Math.sqrt(pmt);
				sUM[u][m] = sUM[u][m] / (put * pmt);
			}
		}
		// similarity of movies and groups
		for(int m=0; m<M; m++){
			for(int g=0; g<G; g++){
				sMG[m][g]=0;
				double pmt=0;
				double pgt=0;
				for (int z=0; z<Z; z++){
					sMG[m][g] = sMG[m][g] + npMZ[m][z] * npGZ[g][z];
					pmt = pmt + npMZ[m][z] * npMZ[m][z];
					pgt = pgt + npGZ[g][z] * npGZ[g][z];	
				}
				pmt=Math.sqrt(pmt);
				pgt=Math.sqrt(pgt);
				
				sMG[m][g]=sMG[m][g]/(pmt*pgt);
			}
		}
		// similarity of groups and users
		for (int g=0;g<G; g++){
			for (int u=0; u<U; u++){
				sGU[g][u]=0;
				double pgt = 0;
				double put = 0;
				for (int z=0; z<Z; z++){
					sGU[g][u] = sGU[g][u] + npGZ[g][z] * npUZ[u][z];
					pgt = pgt + npGZ[g][z] * npGZ[g][z];
					put = put + npUZ[u][z] * npUZ[u][z];
				}
				pgt = Math.sqrt(pgt);
				put = Math.sqrt(put);
				sGU[g][u] = sGU[g][u]/(pgt*put);
				
			}
		}
		
		// movie - group- user
		for (int m=0; m<M; m++){
			for (int u=0; u<U; u++){
				sMGU[m][u] = 0;
				for(int g=0; g<G; g++){
				sMGU[m][u] = sMGU[m][u] + sMG[m][g]*sGM[g][m];
				}
			}
		}
		
		// movie-user-group
		for (int m=0; m<M; m++){
			for(int g=0; g<G; g++){
				sMUG[m][g]=0;
				for (int u=0; u<U; u++){
					
					sMUG[m][g]=sMUG[m][g]+sMU[m][u]*sUG[u][g];
				}	
			}
		}	
		
		// random walk （随机初始化）
		double[][] sGG = new double[G+M][G+M];
		double[] gmrating = new double[G+M];
		Random r = new Random();
		for (int i = 0; i < (G+M); i++) {
				gmrating[i] = r.nextDouble();
		}
		for(int i=0; i<G; i++){
			for (int j=0; j<M; j++){
			sGG[i][i]=0;
			sGG[i][G+j]=sGM[i][j];
			sGG[G+j][i]=sMUG[j][i];
			sGG[G+j][G+j]=0;
			}
		}
	}

	public void RwrforRecommendation() {
		rATM_unRWR();

	}

	public void writeOutput(String filename) throws Exception {
		FileWriter fw = new FileWriter("AT.assign");
		BufferedWriter bw = new BufferedWriter(fw);

		for (int m = 0; m < M; m++) {
			int movieId = moviesId[m];
			for (int n = 0; n < movies[m].length; n++) {
				int word = wordMapInv.get(movies[m][n]);
				int userid = auMapInv.get(moviesU[m][n]);
				bw.write(movieId + "\t" + word + "\t" + userid);
				bw.newLine();
			}
		}

		bw.close();
		fw.close();
	}

	@Override
	public void readDocs(String filename) throws Exception {
		System.out.println("Reading input...");
		try {
			Class.forName("com.mysql.jdbc.Driver"); // 加载MYSQL JDBC驱动程序
	
			System.out.println("Success loading Mysql Driver!");
		} catch (Exception e) {
			System.out.print("Error loading Mysql Driver!");
			e.printStackTrace();
		}

		try {
			Connection connect = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/rs_train_group", "admin",
					"123");
			// 连接URL为 jdbc:mysql//服务器地址/数据库名 ，后面的2个参数分别是登陆用户名和密码

			System.out.println("Success connect Mysql server!");
			Statement stmt = connect.createStatement();
			String str = "select movieid from movie_content"; 
			ResultSet rs = stmt.executeQuery(str); 
			List<Integer> movieList = new ArrayList<Integer>(); 
			while (rs.next()) {

				int movieId = Integer.parseInt(rs.getString("movieid"));
				System.out.println(movieId);
				movieList.add(movieId);
			}

			M = movieList.size();
			movies = new int[M][];
			movieUsers = new int[M][];
			moviesId = movieList.toArray(new Integer[0]);
			for (int m = 0; m < moviesId.length; m++) {
				int movieId = moviesId[m];
				System.out.println("Loading doc: " + movieId + " Progress: " + m
						+ "/" + moviesId.length);
				ResultSet rs2 = stmt
						.executeQuery("select userid from user_movie where movieid="
								+ movieId);
				int MusCnt = 0;
				while (rs2.next()) {
					MusCnt++;
				}
				movieUsers[m] = new int[MusCnt];
				for (int na = MusCnt - 1; rs2.previous(); na--) {
					int userId = Integer.parseInt(rs2.getString("userid"));
					int key = auMap.size();
					if (!auMap.containsKey(userId)) {
						auMap.put(userId, new Integer(key));
						auMapInv.put(new Integer(key), userId);
					} else {
						key = auMap.get(userId).intValue();
					}
					movieUsers[m][na] = key;
				}
				ResultSet rs3 = stmt
						.executeQuery("select wordid from movie_word where movieid="
								+ movieId);

				int wCnt = 0;
				while (rs3.next()) {
					wCnt++;
				}
				movies[m] = new int[wCnt];
				for (int wn = wCnt - 1; rs3.previous(); wn--) {
					int word = Integer.parseInt(rs3.getString("wordid"));
					int key = wordMap.size();
					if (!wordMap.containsKey(word)) {
						wordMap.put(word, new Integer(key));
						wordMapInv.put(new Integer(key), word);
					} else {
						key = ((Integer) wordMap.get(word)).intValue(); 
															
					}
					movies[m][wn] = key;
				}
			}

			// 统计group-users 信息

			Statement stmtg = connect.createStatement();// g
			String strg = "select householdid from household ";// g
			ResultSet rsg = stmtg.executeQuery(strg); // g
			List<Integer> groupList = new ArrayList<Integer>();// g

			while (rsg.next()) {

				int groupId = Integer.parseInt(rsg.getString("householdid"));
				System.out.println(groupId);
				groupList.add(groupId);
			}

			G = groupList.size();
			groupUsers = new int[G][];
			groupsId = groupList.toArray(new Integer[0]);// put the array to the 数组 

			for (int g = 0; g < groupsId.length; g++) {
				int groupId = groupsId[g];
				System.out.println("Loading group: " + groupId + " Progress: "
						+ g + "/" + groupsId.length);
				ResultSet rsg2 = stmtg
						.executeQuery("select userid from household_user where householdid="
								+ groupId);
				int usCnt = 0;
				while (rsg2.next()) {
					usCnt++;
				}

				groupUsers[g] = new int[usCnt];
				for (int nu = usCnt - 1; rsg2.previous(); nu--) {
					int usId = Integer.parseInt(rsg2.getString("userid"));
					int key = usMap.size();
					if (!usMap.containsKey(usId)) {
						usMap.put(usId, new Integer(key));
						usMapInv.put(new Integer(key), usId);
					} else {
						key = usMap.get(usId).intValue();
					}
					groupUsers[g][nu] = key;
				}

			}
			U = auMap.size();
			
////			// 统计user-movie 信息
			Statement stmtu = connect.createStatement();
			String stru = "select userid from user";
			ResultSet rsu = stmtu.executeQuery(stru);
			List<Integer> userList = new ArrayList<Integer>();
			while (rsu.next()) {
				int userId = Integer.parseInt(rsu.getString("userid"));
				System.out.println(userId);
				userList.add(userId);
			}
			Um = userList.size();
			userMovies = new int[Um][];
			usersId = userList.toArray(new Integer[0]);
			for (int u = 0; u < usersId.length; u++) {
				int userId = usersId[u];
//				System.out.println("Loading user: " + userId + " Progress: "
//						+ u + "/" + usersId.length);
				ResultSet rsu2 = stmtu
						.executeQuery("select movieid from user_movie where userid="
								+ userId);
				int mCnt = 0;
				while (rsu2.next()) {
					mCnt++;
				}
				userMovies[u] = new int[mCnt];
				for (int nm = mCnt - 1; rsu2.previous(); nm--) {
					int mId = Integer.parseInt(rsu2.getString("movieid"));
					int key = mMap.size();
					if (!mMap.containsKey(mId)) {
						mMap.put(mId, new Integer(key));
						mMapInv.put(new Integer(key), mId);
					} else {
						key = mMap.get(mId).intValue();
					}
					userMovies[u][nm] = key;
				}

			}
			Mu=mMap.size();

			// user-movie-rating
			umRating = new int[U][M];
			System.out.println(U+"------------------------------");
			System.out.println(M+"####################");
			Statement stmtumr = connect.createStatement();
			String strumr = "select * from user_movie";
			ResultSet rsumr = stmtumr.executeQuery(strumr);
			while (rsumr.next()) {
				int userId = Integer.parseInt(rsumr.getString("userid"));
				int movieid = Integer.parseInt(rsumr.getString("movieid"));
				int rating = Integer.parseInt(rsumr.getString("value"));
//				System.out.print("----------------"+usMap.toString()+"----------------\n");
//				System.out.print("+++++++++++++++++"+userId+"+++++++++++++++++");
				Integer uInv = auMap.get(userId);
				System.out.println( uInv+"&&&&&&&&&&&&&&&&&");
				if (uInv==null){
					
				}
				else{
					int mInv = mMap.get(movieid);
					System.out.println(mInv+"*******************");
					umRating[uInv][mInv] = rating;
				}
				
			}
		} catch (Exception e) {
			System.out.print("get data error!");
			e.printStackTrace();
		}

		W = wordMap.size();
		Ug = usMap.size();
		
		
		System.out.println(M + " documents");
		System.out.println(W + " word types");
		System.out.println(G + "groups");
		System.out.println(U + "users");
		System.out.println(Ug + "users of groups");
		System.out.println(Mu + "movies rated by users");
		System.out.println(Um+ "users of groups who rated movies ");
	}

	@Override
	public void likelihood() {
		double llh = 0;
		for (int m = 0; m < M; m++) {
			double ad = 1.0 / movieUsers[m].length;
			for (int n = 0; n < movies[m].length; n++) {
				double mAa = 0;
				int w = movies[m][n];
				for (int u = 0; u < movieUsers[m].length; u++) {
					int user = this.movieUsers[m][u];
					for (int z = 0; z < Z; z++) {
						mAa += ((nUZW[user][z][w] + beta) / (nUZ[user][z] + W
								* beta))
								* ((nuUZ[user][z] + alpha) / (nU[user] + Z
										* alpha));
						if (mAa * ad > 1) {
							System.out.println("nZWU: " + nUZW[user][z][w]
									+ ", nZU: " + nUZ[user][z]);
							System.out.println(mAa);
						}
					}
				}
				llh += Math.log(ad) + Math.log(mAa);
			}
		}
		// System.out.println("Log-Likelihood: " + llh);
		System.out.println(llh);
	}
}
